package com.franklin.java.completion;

import com.franklin.java.api.annotation.BaseAnnotation;
import com.franklin.java.api.psi.data.PsiCallingElement;
import com.franklin.java.completion.convert.JavaDataConvertCompletionContributor;
import com.franklin.java.completion.method.entity.JavaEntityPropertiesCompletionContributor;
import com.franklin.java.completion.map.MapDataCompletionContributor;
import com.franklin.java.completion.method.param.MybatisPlusWrapperCompletionContributor;
import com.franklin.java.completion.validation.JavaDataValidationCompletionContributor;

import com.franklin.java.utils.CompletionUtils;
import com.franklin.java.utils.PsiUtils;
import com.franklin.plugins.common.utils.DataCheckUtils;
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.completion.impl.CamelHumpMatcher;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementWeigher;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiType;
import com.intellij.psi.impl.light.LightMethodBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;


/**
 * @author Franklin
 * @since 2021/8/5 11:22
 */
public class JavaDataCompletionContributorSupport extends AbstractJavaDataCompletionContributor {

    private ContributorMap contributorMap = new ContributorMap();

    public JavaDataCompletionContributorSupport() {
        BaseAnnotation.initialize();
        contributorMap.put(new JavaDataValidationCompletionContributor());
        contributorMap.put(new JavaDataConvertCompletionContributor());
        contributorMap.put(MapDataCompletionContributor.getInstance());
        contributorMap.put(new JavaEntityPropertiesCompletionContributor());
        contributorMap.put(new MybatisPlusWrapperCompletionContributor());
    }

    @Override
    public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) {
        try {
            if (parameters.getCompletionType() != CompletionType.BASIC) {
                return;
            }
            setCurrentProject(parameters);
            findCallingElement(parameters);
            PsiElement inputElement = findInputAfterDot(parameters);

            LinkedHashSet<CompletionResult> systemResults = result.runRemainingContributors(parameters, false);
            for (CompletionResult systemResult : systemResults) {
                LookupElement lookupElement = systemResult.getLookupElement();
                String lookupString = lookupElement.getLookupString();
                if (lookupString.equals("getClass")) {
                    PsiElement psiElement = lookupElement.getPsiElement();
                    if (psiElement instanceof LightMethodBuilder) {
                        LightMethodBuilder lightMethodBuilder = (LightMethodBuilder) psiElement;
                        PsiType returnType = lightMethodBuilder.getReturnType();
                        if (Objects.nonNull(returnType)) {
                            callingElementType.set(PsiUtils.resolveTypeNameFromGetClassMethodResult(returnType.getCanonicalText()));
                        }
                    }
                }else {
                    //get方法
                    addFranklinElement(lookupElement);
                }
            }

            PrefixMatcher prefixMatcher = null;
            if (Objects.nonNull(inputElement)) {
                String inputText = inputElement.getText();
                prefixMatcher = new CamelHumpMatcher(inputText,false);
            }else {
                prefixMatcher = PrefixMatcher.ALWAYS_TRUE;
            }
            CompletionSorter sorter = CompletionSorter.defaultSorter(parameters, prefixMatcher);

            if (!isCorrectCompletionParameter(parameters)) {
                systemResults.forEach(result::passResult);
                return;
            }

            List<JavaCompletionContributor> low = contributorMap.getOrDefault(CompletionOrder.LOW, Collections.emptyList());
            List<JavaCompletionContributor> mid = contributorMap.getOrDefault(CompletionOrder.MID, Collections.emptyList());
            List<JavaCompletionContributor> high = contributorMap.getOrDefault(CompletionOrder.HIGH, Collections.emptyList());



            doFillCompletionVariants(parameters, result, low, -999999999999d,sorter, prefixMatcher);


            doFillCompletionVariants(parameters, result, mid, 555555555555d,sorter, prefixMatcher);

            doFillCompletionVariants(parameters, result, high, 99999999999999d,sorter, prefixMatcher);

            Set<String> franklinHandledMethods = getFranklinHandledElements();
            systemResults.forEach(completionResult -> doFillSystemCompletionResult(franklinHandledMethods,result, 555555555555d, completionResult.getLookupElement()));
        } finally {
            release();
            releaseFranklinHandleElements();
        }
    }

    @Override
    public List<LookupElement> getLookupElements(CompletionParameters parameters) {
        //do nothing
        return null;
    }

    protected void doFillCompletionVariants(
        CompletionParameters parameters,
        CompletionResultSet result,
        JavaCompletionContributor javaCompletionContributor,
        double priority,
        CompletionSorter sorter,
        PrefixMatcher camelHumpMatcher
    ) {
        List<LookupElement> lookupElements = javaCompletionContributor.getLookupElements(parameters);
        if (DataCheckUtils.isNotEmpty(lookupElements)) {
            lookupElements.forEach(lookupElement -> doFillCompletionResultCore(result, priority, lookupElement,sorter, camelHumpMatcher));
        }
    }

    protected void doFillCompletionResultCore(
            CompletionResultSet result,
            double priority,
            LookupElement lookupElement,
            CompletionSorter sorter,
            @NotNull PrefixMatcher camelHumpMatcher) {

        LookupElement element = CompletionUtils.getPrioritizedLookupElement(lookupElement, priority);
        CompletionResult completionResult = CompletionResult.wrap(element, camelHumpMatcher, sorter);
        if (Objects.nonNull(completionResult)) {
            result.passResult(completionResult);
        }
    }

    protected void doFillSystemCompletionResult(
        Set<String> getMethodStringSet,
        CompletionResultSet result,
        double priority,
        LookupElement lookupElement
    ){
        if (!getMethodStringSet.contains(lookupElement.getLookupString())) {
            LookupElement element = CompletionUtils.getPrioritizedLookupElement(lookupElement, priority);
            result.addElement(element);
        }
    }


    protected void doFillCompletionVariants(
            CompletionParameters parameters,
            CompletionResultSet result,
            Collection<JavaCompletionContributor> javaCompletionContributors,
            double priority,
            CompletionSorter sorter,
            PrefixMatcher camelHumpMatcher
    ) {
        if (DataCheckUtils.isNotEmpty(javaCompletionContributors)) {
            javaCompletionContributors.forEach(contributor -> {
                if (contributor.isCorrectCompletionParameter(parameters)) {
                    doFillCompletionVariants(parameters, result, contributor, priority,sorter, camelHumpMatcher);
                }
            });
        }
    }
}
